D:\git\skunkworks\herald-for-cpp\herald\include\herald\datatype\memory_arena.h
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright 2021 Herald Project Contributors |
2 | | // SPDX-License-Identifier: Apache-2.0 |
3 | | // |
4 | | |
5 | | #ifndef HERALD_MEMORY_ARENA_H |
6 | | #define HERALD_MEMORY_ARENA_H |
7 | | |
8 | | #include <cstddef> |
9 | | #include <bitset> |
10 | | #include <array> |
11 | | |
12 | | /// \brief Acts as a non-global memory arena for arbitrary classes |
13 | | namespace herald { |
14 | | namespace datatype { |
15 | | |
16 | | /// \brief Represents an external 'pointer' to an allocated memory area. |
17 | | /// |
18 | | /// Used by calling classes only. |
19 | | /// Max memory allocation in bytes is 65536 |
20 | | struct MemoryArenaEntry { |
21 | | unsigned short startPageIndex = 0; |
22 | | unsigned short byteLength = 0; |
23 | | |
24 | 20.2k | bool isInitialised() const { |
25 | 20.2k | return 0 != byteLength; |
26 | 20.2k | } |
27 | | }; |
28 | | |
29 | | constexpr unsigned long pagesRequired(std::size_t size,std::size_t pageSize) |
30 | 1.97k | { |
31 | 1.97k | return (size + pageSize - 1) / pageSize; |
32 | 1.97k | } |
33 | | |
34 | | /// \brief Very basic paged memory arena class |
35 | | /// |
36 | | /// Can be used one arena per dynamic allocation class, or used by multiple classes. |
37 | | /// In this non-global implementation, pass it as a static reference variable to the class |
38 | | /// once during application startup after allocation in a main class or similar. |
39 | | template <std::size_t MaxSize, std::size_t AllocationSize> |
40 | | class MemoryArena { |
41 | | public: |
42 | | /// \brief The Maximum size to use for data (doesn't include page table) |
43 | | static constexpr std::size_t Size = MaxSize; |
44 | | /// \brief The allocation size for each page. Note total mem usage is Size + (Size / PageSize) |
45 | | /// Thus for MemoryArena<2048,10>() you use 2048 + (2048 / 10) = 2253 bytes |
46 | | static constexpr std::size_t PageSize = AllocationSize; |
47 | | |
48 | | MemoryArena() noexcept |
49 | | : arena(), pagesInUse(false) |
50 | 5 | { |
51 | 5 | ; |
52 | 5 | } ??0?$MemoryArena@$0IAA@$09@datatype@herald@@QEAA@XZ Line | Count | Source | 50 | 4 | { | 51 | 4 | ; | 52 | 4 | } |
??0?$MemoryArena@$0CAAA@$07@datatype@herald@@QEAA@XZ Line | Count | Source | 50 | 1 | { | 51 | 1 | ; | 52 | 1 | } |
|
53 | | |
54 | | ~MemoryArena() = default; |
55 | | |
56 | 265 | void reserve(MemoryArenaEntry& entry,std::size_t newSize) { |
57 | 265 | if (newSize <= entry.byteLength) { |
58 | 5 | return; |
59 | 5 | } |
60 | 260 | auto newEntry = allocate(newSize); |
61 | 1.73k | for (std::size_t i = 0;i < entry.byteLength;++i1.47k ) { |
62 | 1.47k | set(newEntry,i,get(entry,i)); |
63 | 1.47k | } |
64 | 260 | deallocate(entry); |
65 | 260 | entry = newEntry; |
66 | 260 | } ?reserve@?$MemoryArena@$0IAA@$09@datatype@herald@@QEAAXAEAUMemoryArenaEntry@23@_K@Z Line | Count | Source | 56 | 3 | void reserve(MemoryArenaEntry& entry,std::size_t newSize) { | 57 | 3 | if (newSize <= entry.byteLength) { | 58 | 2 | return; | 59 | 2 | } | 60 | 1 | auto newEntry = allocate(newSize); | 61 | 11 | for (std::size_t i = 0;i < entry.byteLength;++i10 ) { | 62 | 10 | set(newEntry,i,get(entry,i)); | 63 | 10 | } | 64 | 1 | deallocate(entry); | 65 | 1 | entry = newEntry; | 66 | 1 | } |
?reserve@?$MemoryArena@$0CAAA@$07@datatype@herald@@QEAAXAEAUMemoryArenaEntry@23@_K@Z Line | Count | Source | 56 | 262 | void reserve(MemoryArenaEntry& entry,std::size_t newSize) { | 57 | 262 | if (newSize <= entry.byteLength) { | 58 | 3 | return; | 59 | 3 | } | 60 | 259 | auto newEntry = allocate(newSize); | 61 | 1.72k | for (std::size_t i = 0;i < entry.byteLength;++i1.46k ) { | 62 | 1.46k | set(newEntry,i,get(entry,i)); | 63 | 1.46k | } | 64 | 259 | deallocate(entry); | 65 | 259 | entry = newEntry; | 66 | 259 | } |
|
67 | | |
68 | 1.06k | MemoryArenaEntry allocate(std::size_t size) { |
69 | 1.06k | if (0 == size) { |
70 | 70 | return MemoryArenaEntry{0,0}; |
71 | 70 | } |
72 | 999 | // find first page location with enough space |
73 | 999 | unsigned long pages = pagesRequired(size,PageSize); |
74 | 999 | bool inEmpty = false; |
75 | 999 | unsigned long lastEmptyIndex = 0; |
76 | 39.6k | for (std::size_t i = 0;i < pagesInUse.size();++i38.6k ) { |
77 | 39.6k | if (!pagesInUse.test(i)) { |
78 | 1.80k | // this one is empty |
79 | 1.80k | if (!inEmpty) { |
80 | 1.13k | inEmpty = true; |
81 | 1.13k | lastEmptyIndex = i; |
82 | 1.13k | } |
83 | 1.80k | if (i - lastEmptyIndex + 1 == pages) { |
84 | 998 | // flip bits and return |
85 | 2.65k | for (unsigned long f = lastEmptyIndex;f <= i;++f1.65k ) { |
86 | 1.65k | pagesInUse.set(f,true); |
87 | 1.65k | } |
88 | 998 | return MemoryArenaEntry{(unsigned short)lastEmptyIndex,(unsigned short)size}; |
89 | 998 | } |
90 | 37.8k | } else { |
91 | 37.8k | inEmpty = false; |
92 | 37.8k | } |
93 | 39.6k | } |
94 | 999 | // ran out of memory! Throw! (Causes catastrophic crash) |
95 | 999 | throw std::runtime_error("Unable to allocate memory in arena")1 ; |
96 | 999 | } ?allocate@?$MemoryArena@$0IAA@$09@datatype@herald@@QEAA?AUMemoryArenaEntry@23@_K@Z Line | Count | Source | 68 | 9 | MemoryArenaEntry allocate(std::size_t size) { | 69 | 9 | if (0 == size) { | 70 | 0 | return MemoryArenaEntry{0,0}; | 71 | 0 | } | 72 | 9 | // find first page location with enough space | 73 | 9 | unsigned long pages = pagesRequired(size,PageSize); | 74 | 9 | bool inEmpty = false; | 75 | 9 | unsigned long lastEmptyIndex = 0; | 76 | 640 | for (std::size_t i = 0;i < pagesInUse.size();++i631 ) { | 77 | 639 | if (!pagesInUse.test(i)) { | 78 | 221 | // this one is empty | 79 | 221 | if (!inEmpty) { | 80 | 9 | inEmpty = true; | 81 | 9 | lastEmptyIndex = i; | 82 | 9 | } | 83 | 221 | if (i - lastEmptyIndex + 1 == pages) { | 84 | 8 | // flip bits and return | 85 | 228 | for (unsigned long f = lastEmptyIndex;f <= i;++f220 ) { | 86 | 220 | pagesInUse.set(f,true); | 87 | 220 | } | 88 | 8 | return MemoryArenaEntry{(unsigned short)lastEmptyIndex,(unsigned short)size}; | 89 | 8 | } | 90 | 418 | } else { | 91 | 418 | inEmpty = false; | 92 | 418 | } | 93 | 639 | } | 94 | 9 | // ran out of memory! Throw! (Causes catastrophic crash) | 95 | 9 | throw std::runtime_error("Unable to allocate memory in arena")1 ; | 96 | 9 | } |
?allocate@?$MemoryArena@$0CAAA@$07@datatype@herald@@QEAA?AUMemoryArenaEntry@23@_K@Z Line | Count | Source | 68 | 1.06k | MemoryArenaEntry allocate(std::size_t size) { | 69 | 1.06k | if (0 == size) { | 70 | 70 | return MemoryArenaEntry{0,0}; | 71 | 70 | } | 72 | 990 | // find first page location with enough space | 73 | 990 | unsigned long pages = pagesRequired(size,PageSize); | 74 | 990 | bool inEmpty = false; | 75 | 990 | unsigned long lastEmptyIndex = 0; | 76 | 38.9k | for (std::size_t i = 0;i < pagesInUse.size();++i38.0k ) { | 77 | 38.9k | if (!pagesInUse.test(i)) { | 78 | 1.58k | // this one is empty | 79 | 1.58k | if (!inEmpty) { | 80 | 1.12k | inEmpty = true; | 81 | 1.12k | lastEmptyIndex = i; | 82 | 1.12k | } | 83 | 1.58k | if (i - lastEmptyIndex + 1 == pages) { | 84 | 990 | // flip bits and return | 85 | 2.42k | for (unsigned long f = lastEmptyIndex;f <= i;++f1.43k ) { | 86 | 1.43k | pagesInUse.set(f,true); | 87 | 1.43k | } | 88 | 990 | return MemoryArenaEntry{(unsigned short)lastEmptyIndex,(unsigned short)size}; | 89 | 990 | } | 90 | 37.4k | } else { | 91 | 37.4k | inEmpty = false; | 92 | 37.4k | } | 93 | 38.9k | } | 94 | 990 | // ran out of memory! Throw! (Causes catastrophic crash) | 95 | 990 | throw std::runtime_error("Unable to allocate memory in arena")0 ; | 96 | 990 | } |
|
97 | | |
98 | 1.37k | void deallocate(MemoryArenaEntry& entry) { |
99 | 1.37k | if (!entry.isInitialised()) { |
100 | 422 | return; // guard |
101 | 422 | } |
102 | 954 | // set relevant bits to empty |
103 | 954 | long pages = pagesRequired(entry.byteLength,PageSize); |
104 | 2.55k | for (int i = 0;i < pages;++i1.60k ) { |
105 | 1.60k | pagesInUse.set(entry.startPageIndex + i,false); |
106 | 1.60k | } |
107 | 954 | entry.byteLength = 0; |
108 | 954 | entry.startPageIndex = 0; |
109 | 954 | } ?deallocate@?$MemoryArena@$0IAA@$09@datatype@herald@@QEAAXAEAUMemoryArenaEntry@23@@Z Line | Count | Source | 98 | 4 | void deallocate(MemoryArenaEntry& entry) { | 99 | 4 | if (!entry.isInitialised()) { | 100 | 0 | return; // guard | 101 | 0 | } | 102 | 4 | // set relevant bits to empty | 103 | 4 | long pages = pagesRequired(entry.byteLength,PageSize); | 104 | 210 | for (int i = 0;i < pages;++i206 ) { | 105 | 206 | pagesInUse.set(entry.startPageIndex + i,false); | 106 | 206 | } | 107 | 4 | entry.byteLength = 0; | 108 | 4 | entry.startPageIndex = 0; | 109 | 4 | } |
?deallocate@?$MemoryArena@$0CAAA@$07@datatype@herald@@QEAAXAEAUMemoryArenaEntry@23@@Z Line | Count | Source | 98 | 1.37k | void deallocate(MemoryArenaEntry& entry) { | 99 | 1.37k | if (!entry.isInitialised()) { | 100 | 422 | return; // guard | 101 | 422 | } | 102 | 950 | // set relevant bits to empty | 103 | 950 | long pages = pagesRequired(entry.byteLength,PageSize); | 104 | 2.34k | for (int i = 0;i < pages;++i1.39k ) { | 105 | 1.39k | pagesInUse.set(entry.startPageIndex + i,false); | 106 | 1.39k | } | 107 | 950 | entry.byteLength = 0; | 108 | 950 | entry.startPageIndex = 0; | 109 | 950 | } |
|
110 | | |
111 | 8.91k | void set(const MemoryArenaEntry& entry, unsigned short bytePosition, unsigned char value) { |
112 | 8.91k | if (!entry.isInitialised()) { |
113 | 0 | return; |
114 | 0 | } |
115 | 8.91k | arena[(entry.startPageIndex * PageSize) + bytePosition] = value; |
116 | 8.91k | } ?set@?$MemoryArena@$0IAA@$09@datatype@herald@@QEAAXAEBUMemoryArenaEntry@23@GE@Z Line | Count | Source | 111 | 102 | void set(const MemoryArenaEntry& entry, unsigned short bytePosition, unsigned char value) { | 112 | 102 | if (!entry.isInitialised()) { | 113 | 0 | return; | 114 | 0 | } | 115 | 102 | arena[(entry.startPageIndex * PageSize) + bytePosition] = value; | 116 | 102 | } |
?set@?$MemoryArena@$0CAAA@$07@datatype@herald@@QEAAXAEBUMemoryArenaEntry@23@GE@Z Line | Count | Source | 111 | 8.81k | void set(const MemoryArenaEntry& entry, unsigned short bytePosition, unsigned char value) { | 112 | 8.81k | if (!entry.isInitialised()) { | 113 | 0 | return; | 114 | 0 | } | 115 | 8.81k | arena[(entry.startPageIndex * PageSize) + bytePosition] = value; | 116 | 8.81k | } |
|
117 | | |
118 | 9.93k | char get(const MemoryArenaEntry& entry, unsigned short bytePosition) { |
119 | 9.93k | if (!entry.isInitialised()) { |
120 | 0 | return '\0'; |
121 | 0 | } |
122 | 9.93k | return arena[(entry.startPageIndex * PageSize) + bytePosition]; |
123 | 9.93k | } ?get@?$MemoryArena@$0IAA@$09@datatype@herald@@QEAADAEBUMemoryArenaEntry@23@G@Z Line | Count | Source | 118 | 17 | char get(const MemoryArenaEntry& entry, unsigned short bytePosition) { | 119 | 17 | if (!entry.isInitialised()) { | 120 | 0 | return '\0'; | 121 | 0 | } | 122 | 17 | return arena[(entry.startPageIndex * PageSize) + bytePosition]; | 123 | 17 | } |
?get@?$MemoryArena@$0CAAA@$07@datatype@herald@@QEAADAEBUMemoryArenaEntry@23@G@Z Line | Count | Source | 118 | 9.92k | char get(const MemoryArenaEntry& entry, unsigned short bytePosition) { | 119 | 9.92k | if (!entry.isInitialised()) { | 120 | 0 | return '\0'; | 121 | 0 | } | 122 | 9.92k | return arena[(entry.startPageIndex * PageSize) + bytePosition]; | 123 | 9.92k | } |
|
124 | | |
125 | 12 | std::size_t pagesFree() const { |
126 | 12 | return pagesRequired(Size,PageSize) - pagesInUse.count(); |
127 | 12 | } |
128 | | |
129 | | private: |
130 | | std::array<unsigned char,Size> arena; |
131 | | std::bitset<pagesRequired(Size,PageSize)> pagesInUse; |
132 | | }; |
133 | | |
134 | | } |
135 | | } |
136 | | |
137 | | #endif |